Authors: Stephen Sekula
In this python notebook, we will explore numbers, especially vectors - numbers that define both a length and a direction. Vectors are a crucial class of numbers in physics. Many quantities in the natural world must be specified by more than just a size or a length; they indicate where to go next, and also need to specify directional information. In this unit, we explore simple numbers without direction ("scalars"), then move on to numbers with direction ("vectors"). This will all be done with a programming language, called PYTHON, which you will use over and over this semester to explore different questions about the natural world.
In this unit, you will learn the following:
Sections below are marked by one of three graphics: green circle, blue square, and black diamond. If you have ever gone skiing before, you might recognize these as ski trail difficulty markers. "Green circle" indicates a beginner-level trail; "Blue square" is intermediate-level; "Black diamond" is advanced level.
Everyone should be able to make progress on green circle material. Once you have a good comfort level with green circle, blue square will feel much more approachable. Black diamond may challenge even experienced people, but nonetheless it is important to see what is possible and aspire!
A "scalar" is a number with magnitude, but no directions. Here are some physical quantities that are represented by scalars:
Now that you have familiarized yourself with scalars in the physical world, let us represent them using the PYTHON programming language and then learn to operate on scalars to do things - like make other scalars.
PYTHON has two built-in simple scalar types: integers (0,1,2,3,... or -1,-2,-3,...) and "floating point" numbers, more commonly known as "decimal numbers" (1.1, 3.14159, 75.008). PYTHON allows you to have a symbol represent a number (like in algebra) and then use the symbol to conduct operations on the numbers. Consider the following first steps into PYTHON to store a decimal number using a "variable" (a symbol that stands for a number and whose value can be altered), and then act on the variable with mathematical operations (addition, +, subtraction, -, multiplication, *, and division, /).
To make the next block of "code" (programming commands) do something, click your mouse cursor into the block and press the SHIFT key and the ENTER key at the same time.
In [1]:
# This is a comment. PYTHON ignores these, but they are helpful for people!
# Any line that begins with a # symbol is a comment.
# Let's define a variable, x:
x = 5.0
# By just placing the variable on its own line, we can print its value:
x
# That's not very pretty. Just a number on a line. Let's print this with some helpful text:
print("The value of x is %f" %(x))
# How about printing a multiplication operation?
print("%f" %(x*5.0))
# We don't need all those decimal places. Let's print just a few (or maybe lots more! Play around!)
print("%.2f" % (x*5.0))
# Let's define a new variable, y, based on x:
y = x*2.0
print("The value of y is:")
print(y)
# Let's print out some arithmetic operations between x and y:
print("Addition")
print(x+y)
print("Subtraction")
print(x-y)
print("Multiplication")
print(x*y)
print("Division")
print(x/y)
# Congratulations - you just learned how to make a computer do whatever you want. Imagine the endless applications!
A "vector" is a collection of scalars that, together, represent both magnitude and direction. Many physical quantities can only be accurately described by vectors. For example:
There are many notations for a vector. For instance, let us try to represent the first example above - velocity - using a notation.
"I am driving northeast at 70 miles per hour."
Let us assume that the driver is moving an equal amount of distance north for every amount west that they move. We can represent one step in their motion as follows:
In [2]:
# We'll need this more later, but this code snippet will draw arrows. We need them to represent the above statement
import matplotlib.pyplot as plt
import math
%matplotlib inline
plt.axis('on')
plt.xlim(-1.0,1.0)
plt.ylim(-1.0,1.0)
# Draw an east-pointing arrow
plt.arrow(0, 0, 1/math.sqrt(2)-0.1, 0, head_width=0.05, head_length=0.1, fc='k', ec='k');
# Draw a north-pointing arrow
plt.arrow(1/math.sqrt(2), 0, 0, 1/math.sqrt(2)-0.1, head_width=0.05, head_length=0.1, fc='k', ec='k');
# Draw the actual arrow representing the total motion of the car
plt.arrow(0, 0, 0.9/math.sqrt(2), 0.9/math.sqrt(2), head_width=0.05, head_length=0.1, fc='c', ec='c');
The above code snippet will help us later in doing more vector visualization, but for now focus on notating the above picture. For every bit of distance, x, that we go eastward, we go a distance, y, northward. In the above picture, x=y . . . but that doesn't always have to be the case. In fact, most of the time it's not.
Let us look at two popular notations for the above:
$\vec{v} = (x,y)$
or
$\vec{v} = x\hat{i} + y\hat{j}$
The first notation uses parentheses and commas to separate the horizontal component (x) from the vertical component (y). The second notation uses an additional kind of vector, a unit vector, to represent "direction along horizontal" ($\hat{i}$) and "direction along vertical" ($\hat{j}$).
For this exercise, we prefer the first notation since it's very similar to what we will do in PYTHON to represent vectors. Let's look at that.
A vector is an arrow. It has direction, but also length ("magnitude"). We now have enough mathematical information to calculate the length of any vector, knowing its components.
We can determine the length of a vector using the Pythagorean Theorem. Consider the picture above of driving east and then driving north. The eastward movement creates a horizontal vector. The northward movement creates a vertical vector. The resulting total motion - driving northeast - is the sum of two independent motions, one eastward and one northward. We can write the eastward vector (of length $e$) as:
\begin{equation} \vec{e} = (e,0) \end{equation}and the northward vector (of length $n$) as:
\begin{equation} \vec{n} = (0,n) \end{equation}If we add them, using the rules above, we arrive at the total motion vector:
\begin{equation} \vec{v} = (e,0) + (0,n) = (e,n). \end{equation}But what is the length of this total vector? The two components form the base and height if a right-triangle. You can imagine, then, that if we know the length of the base and the length of the height, we can find the total length of the hypotenuse - which is just the length ($L$) of the total vector! This would look like this:
\begin{equation} L^2 = e^2 + n^2 \longrightarrow L = \sqrt{e^2 + n^2} \end{equation}Consider the mathematical operations we introduced above. Which operation does the length-squared, $L^2$, resemble?
If you said "dot-product," you're correct. In fact, the dot-product of a vector with its own self gives you the square of the length of that vector:
\begin{equation} {\vec v} \cdot {\vec v} = (e,n) \cdot (e,n) = (e \cdot e) + (n \cdot n) = e^2 + n^2 = L^2 \end{equation}Let us now play with all of this in PYTHON.
Below, we show you how to write vectors using PYTHON and then act on vectors with various algebraic operations. Play around with these and see if you can get a feel for what it means to define and then manipulate a vector. The visualization below will update when you make changes.
In [3]:
# import a library of numerical tools for defining and manipulating vectors
import numpy as np
# Let's define a vector using two scalar variables to set the components
x = 1.0
y = 5.0
v_1 = np.array([x,y])
# Let us print the components of the vector
print("The components of the vector, v_1, are as follows:")
print("x = %f" % (v_1[0]))
print("y = %f" % (v_1[1]))
# We see that adding the operator [] after the vector, with a number inside the square brackets, gains us
# access to the components of the vector.
# Let us define a second vector
v_2 = np.array([4,5])
# Let us do vector addition:
print("Vector addition of v_1 + v_2 yields: (%f, %f)" % ((v_1+v_2)[0], (v_1+v_2)[1]))
# We see that this, indeed, yields what we expect from the definition discussed above!
# Let us subtract them:
print("Vector subtraction of v_1 - v_2 yields: (%f, %f)" % ((v_1-v_2)[0], (v_1-v_2)[1]))
# Let us now multiply the vector by a scalar
a = 5
print("Scalar multiplication of a * v_1 yields: (%f, %f)" % ((a*v_1)[0],(a*v_1)[1]))
# Just as we expected from the definitions above!
# Let us now do the dot-product multiplication of two vectors.
# Numpy provides a special "function" to execute the dot product:
print("Dot-product multiplication of v_1 * v_2 yields: %f" % (np.dot(v_1, v_2)))
# Again, this gives us what we expected from the definition of the dot product.
# Let us now calculate the length of the vector, v_1, in two ways.
# The first way is "brute-force" - do the dot-product multiplication and then square-root it:
length = math.sqrt( np.dot(v_1, v_1))
print("Length Method A: the length of v_1 is %f" % (length))
# The second way is to use numpy's built-in function for computing the length:
length = np.linalg.norm(v_1)
print("Length Method B: the length of v_1 is %f" % (length))
# Oh, look. They are the same!
# Method B utilizes the "Linear Algebra" tools, and specifically the "norm()" function, which is
# designed to return the length of a vector of any size (we used a 2-dimensional vector, but you
# could give it a 10-dimensional vector and it still works!). "Linear Algebra" is a mathematics class
# you might take later, and it's the algebra of structured numbers like vectors and matrices, generically
# known as "tensors".
# Here is a 5-dimensional vector:
v_3 = np.array([1,3,5,7,9])
print("The length of our 5-D vector is: %f" % (np.linalg.norm(v_3)))
# Neat, huh? Try computing the length yourself and see if the above answer is right.
The interactive PYTHON game below lets you play with a vector, interacting by using sliders to change things like the total length of the vector or the lengths of its components. Consider the following critical questions:
In [23]:
from ipywidgets import widgets
from ipywidgets import interact
import matplotlib.pyplot as plt
import math
%matplotlib inline
def f(x):
print(x)
global x_last
x_last = 0.0
global y_last
y_last = 0.0
def draw_vector(x,y):
global x_last
global y_last
plt.axis('on')
plt.xlim(-5.0,5.0)
plt.ylim(-5.0,5.0)
xlength = x
ylength = y
length = math.sqrt(x**2 + y**2)
# Draw the previous vector in a fainter color/shade
if (x_last != 0 and y_last != 0):
length_last = math.sqrt(x_last**2 + y_last**2)
plt.arrow(0, 0, x_last, y_last, head_width=0.2, head_length=0.2, fc='c', ec='c');
# Draw the vector
plt.arrow(0, 0, xlength, ylength, head_width=0.2, head_length=0.2, fc='k', ec='k');
plt.show()
x_last = x
y_last = y
interact(draw_vector,x=widgets.IntSlider(min=-4,max=4,step=1,value=1),y=widgets.IntSlider(min=-4,max=4,step=1,value=1))
print("Note: the current vector is in black, while the previous vector is in cyan (light blue).")
Let us now program a simple "animation" by adding TIME as an additional dimension to the problem. The above graphic of a northeast-pointing vector is static - it represents a moment in frozen time. It never changes. Let's add time as a step, and have some fun with this by making something that moves.
Let us do this. Let us:
How do we do the "bounce"? Let's choose the following:
You can make up your own rules. Here is what the program looks like in PYTHON if we implement these.
Note:
Try playing around with the total number of steps in the game ("total_steps") and the direction and length of the original vector (look for the comment, "initial vector," preceding those lines of code).
In [4]:
# Library setups
import numpy as np
import math
import matplotlib.pyplot as plt
# Define variables accessible (and modifiable) by functions
global board_length
board_length = 1
global board_height
board_height = 1
# colors that we can assign to the arrow and change in the game
global colors
colors=['k','b','g','r','c','m'] # black, blue, red, green, cyan, magenta
global vector_color
vector_color = 0
# Define some functions for determining if our vector is on the board
# A "function" takes input and does something to it. It allows you to
# encapsulate repetative tasks.
# Is the vector's front end inside the bounds of the board's horizontal size?
def vector_in_board_horizontally(x_start = 0, v = np.array([])):
if (x_start + v[0]) < 0:
return -1
if (x_start + v[0]) > board_length:
return 1
return 0
# Is the vector's front end inside the bounds of the board's vertical size?
def vector_in_board_vertically(y_start = 0, v = np.array([])):
if (y_start + v[1]) < 0:
return -1
if (y_start + v[1]) > board_length:
return 1
return 0
# Define some functions for flipping vector components
def flip_horizontal(v = np.array([])):
return np.array([-v[0],v[1]])
def flip_vertical(v = np.array([])):
return np.array([v[0],-v[1]])
# function to draw vector
def draw_vector(x_initial = 0.5, y_initial = 0.5, v = np.array([])):
plt.arrow(x_initial, y_initial, v[0], v[1], head_width=0.05, head_length=0.1, fc=colors[vector_color], ec=colors[vector_color]);
return v
def next_color():
global vector_color
vector_color += 1
if vector_color >= len(colors):
vector_color = 0
return vector_color
# Draw the board and then play the game
# We'll need this more later, but this code snippet will draw arrows. We need them to represent the above statement
import matplotlib.pyplot as plt
import math
%matplotlib inline
plt.axis('on')
plt.xlim(0,board_length)
plt.ylim(0,board_height)
# initial vector
x_start = 0.32
y_start = 0.5
v = draw_vector(x_initial = x_start, y_initial = y_start, v=np.array([0.1,0.2]))
# Place a dot at the starting point - this helps us find the start point!
plt.plot(x_start,y_start, marker='o', color=colors[vector_color], ls='')
# Step the arrow around the board, applying our rules when a vector reaches the boundaries
# When we "bounce" off a wall, change the color!
total_steps = 50
# a "for loop" repeats a task over and over again, up to a limit that you define
for step in range(total_steps):
v_next = v
# update the start point
x_start = x_start + v[0]
y_start = y_start + v[1]
horizontal_choice = vector_in_board_horizontally(x_start, v)
if horizontal_choice == 0:
v_next = v_next
elif horizontal_choice == -1:
v_next = flip_horizontal(v_next)
x_start = 0
next_color()
elif horizontal_choice == 1:
v_next = flip_horizontal(v_next)
x_start = board_length
next_color()
vertical_choice = vector_in_board_vertically(y_start, v)
if vertical_choice == 0:
v_next = v_next
elif vertical_choice == -1:
v_next = flip_vertical(v_next)
y_start = 0
next_color()
elif vertical_choice == 1:
v_next = flip_vertical(v_next)
y_start = board_length
next_color()
v = v_next
draw_vector(x_start, y_start, v)
In [ ]: